function Compile(vm, el) {
  this.vm = vm
  this.el = document.querySelector(el)
  this.fragment = null
  this.radioMap = new Map()
  this.checkboxMap = new Map()
  this.init()
}

Compile.prototype = {
  init: function() {
    if (this.el) {
      this.fragment = this.nodeToFragment(this.el)
      this.compileElements(this.fragment)
      this.el.appendChild(this.fragment)
    } else {
      console.log('未找到节点')
    }
  },
  nodeToFragment: function(el) {
    var fragment = document.createDocumentFragment()
    var childNode = el.firstChild
    while (childNode) {
      fragment.appendChild(childNode)
      childNode = el.firstChild
    }
    return fragment
  },
  compileElements: function(el) {
    var nodeList = el.childNodes;
    var reg = /\{\{(.*)\}\}/;
    [].slice.call(nodeList).forEach(node => {
      // var name = Object.prototype.toString.call(node).match(/\[object (.*?)\]/)[1]
      // console.log(name);
      var content = node.textContent
      if (this.isElementNode(node)) {
        this.compileElementNode(node)
      } else if (this.isTextNode(node) && reg.test(content)) {
        this.compileText(node, reg.exec(content)[1])
      }
      var subNodes = node.childNodes
      if (subNodes !== null && subNodes.length > 0) {
        this.compileElements(node)
      }
    })
  },
  compileElementNode: function(node) {
    var self = this
    var attrbutes = node.attributes
    Array.prototype.forEach.call(attrbutes, function(attr) {
      var attrName = attr.name
      var attrVal = attr.value
      if (self.isDirective(attrName)) {
        var dir = attrName.substring(2) // 时间名
        if (self.isEventDirective(dir)) {
          self.compileEvent(node, self.vm, attrVal, attrName)
        } else {
          self.compileModel(node, self.vm, attrVal)
        }
      }
    })
  },
  compileText: function(node, exp) {
    var self = this;
    var propVal = this.vm[exp]
    this.updateText(node, propVal)
    new Watcher(this.vm, exp, function(newVal) { //callback方法把node和watcher关联起来
      self.updateText(node, newVal)
    })
  },
  compileEvent: function(node, vm, exp, dir) {
    //dir 事件名，exp事件对应的方法名，node 节点 ，vm selfvue对象
    var eventName = dir.substring(5)
    var eventFunc = vm.methods && vm.methods[exp] //从vm的methods去中去拿事件对应的方法
    if (eventName && eventFunc) {
      node.addEventListener(eventName, eventFunc.bind(vm), false) //指定eventFunc的执行作用域为vm
    }
  },
  compileModel: function(node, vm, exp) {
    var self = this
    var old = vm[exp]
    var nodeName = node.nodeName
    var nodeType = node.type
    var nodeId = nodeName.concat("+").concat(nodeType)
    switch (nodeId) {
      case 'INPUT+text':
        this.modelUpdater(node, old) //首先更新input的val值
        new Watcher(vm, exp, function(val) {
          self.modelUpdater(node, val)
        })
        node.addEventListener('input', function(e) {
          var newVal = e.target.value
          if (newVal === old) {
            return
          }
          vm[exp] = newVal
          old = newVal // 将现值改为闭包之前记录的值
        })
        break
      case 'TEXTAREA+textarea':
        this.textUpdater(node, old)
        new Watcher(vm, exp, function(val) {
          self.textUpdater(node, val)
        })
        node.addEventListener('input', function(e) {
          var newVal = e.target.value
          if (newVal === old) {
            return
          }
          vm[exp] = newVal
          old = newVal
        })
        break
      case 'INPUT+radio':
        if (this.radioMap.get(node.name)) {
          return //已经添加过v-model监听
        }
        this.radioMap.set(node.name, exp)
        var nodeList = this.fragment.querySelectorAll("input[type=radio][name=" + node.name + "]")
        this.radioUpdater(nodeList, old)
        new Watcher(vm, exp, function(val) {
          self.radioUpdater(nodeList, val)
        })
        nodeList.forEach(node => {
          node.addEventListener('change', function(e) {
            var newVal = e.target.value
            if (newVal === old) {
              return
            }
            vm[exp] = newVal
            old = newVal
          })
        })
        break
      case 'INPUT+checkbox':
        var vmodelAttr = node.getAttribute("v-model")
        if (this.checkboxMap.get(vmodelAttr)) {
          return //已经添加过v-model监听
        }
        this.checkboxMap.set(vmodelAttr, exp)
        var nodeList = this.fragment.querySelectorAll("input[type=checkbox][v-model=" + vmodelAttr + "]")
        this.checkboxUpdater(nodeList, (old instanceof Array) ? old : [])
        new Watcher(vm, exp, function(array) {
          self.checkboxUpdater(nodeList, array)
        })
        nodeList.forEach(node => {
          node.addEventListener('change', function(e) {
            var newVal = e.target.value
            var newArra = []
            if (this.checked) { //被选中
              newArra = newArra.concat(old)
              newArra.push(newVal)
            } else { //取消选中
              newArra = old.filter(ele => ele !== newVal)
            }
            old = newArra
            vm[exp] = newArra
          })
        })
        break
    }
  },
  updateText: function(node, propVal) {
    node.textContent = typeof propVal == 'undefined' ? '' : propVal
  },
  modelUpdater: function(node, val) {
    node.value = typeof val === 'undefined' ? '' : val
  },
  textUpdater: function(node, val) {
    node.textContent = typeof val === 'undefined' ? '' : val
  },
  radioUpdater: function(nodeList, val) {
    nodeList.forEach(node => {
      if (val === node.value) {
        node.setAttribute("checked", true)
      }
    })
  },
  checkboxUpdater: function(nodeList, val) {
    nodeList.forEach(node => {
      val.forEach(ele => {
        if (node.value === ele) {
          node.setAttribute("checked", true)
        }
      })
    })
  },
  isDirective: function(attr) {
    return attr.indexOf('v-') === 0
  },
  isEventDirective: function(attr) {
    return attr.indexOf('on:') === 0
  },
  isElementNode: function(node) {
    return node.nodeType === 1
  },
  isTextNode: function(node) {
    return node.nodeType === 3
  }
}